﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Grundy.Common;
using Grundy.Entity;
using Grundy.Message;
using log4net;

namespace Grundy.Licensing.Server
{
    public class UncountedConcurrentSessionsStrategy : LicensingStrategy
    {
        private static ILog _logger = LogManager.GetLogger(typeof(UncountedConcurrentSessionsStrategy));
        private const int FORGET_SESSIONS_THRESHOLD = 1000;

        public UncountedConcurrentSessionsStrategy(License license, LicenseEntitlementValidity licenseEntitlementValidity, FeatureIdentificationKey featureIdentificationKey) : base(license, licenseEntitlementValidity, featureIdentificationKey) { }

        protected override Guid CheckoutLicense(LicenseRequestInformation lri, object syncLock)
        {
            var clientIdentifiers = lri.ClientIdentifiers;

            var uncountedFloatingLicense = (UncountedFloatingLicense)License;
            var clientType = uncountedFloatingLicense.Client;

            var clientIdentifiersToUseEnumerable = clientIdentifiers.Where(i =>
            {
                var typeOfIdentifier = i.GetType();
                return clientType.GetIdentifiers().Contains(typeOfIdentifier);
            });
            
            var clientIdentifiersToUse = new IdentifierCollection(clientIdentifiersToUseEnumerable);

            lock (syncLock)
            {   
                var matchingClientSession = clientSessions.Values.Where(i => i.IdentifiersUsedToIdentify.Equals(clientIdentifiersToUse)).FirstOrDefault();
                if (matchingClientSession != null)
                    return matchingClientSession.Id;

                ForgetSessions(clientSessions, FORGET_SESSIONS_THRESHOLD);
                var sessionId = AddClientSession(lri, clientIdentifiersToUse);
                _logger.InfoFormat("Client {0} checked out {1}.", clientIdentifiers, uncountedFloatingLicense);
                return sessionId;
            }
        }

        private void ForgetSessions(ConcurrentDictionary<Guid, ClientSession> clientSessions, int forgetSessionsThreshold)
        {
            if (clientSessions.Count >= forgetSessionsThreshold)
            {
                var sessionsToForget = clientSessions.Values.OrderBy(i => i.LastSeenOn).Select(i => i.Id).Take(forgetSessionsThreshold/2).ToList();
                ClientSession tmp; 
                sessionsToForget.ForEach(i => clientSessions.TryRemove(i, out tmp));
            }
        }
    }
}